(PART) My work

Emotions In Online Customer Reviews

Consumers usually seek quality and relevant information when buying new products. With the expansion and availability of the Internet, online consumer reviews have become a valuable resource to look at. Several studies tried to demystify relationship between product sales and online customer reviews. On the one hand, some of them, such as Senecal and Nantel (2004), suggest that participants who consulted product recommendations selected these products twice as often as those who did not consult recommendations. On the other hand, Zhang and Dellarocas (2006) find that online reviews and do not influence sales and serve solely as prediction.

Between these two opinion fronts, one thing is certain: both sides aim to find out how consumers perceive and process word-of-mouth in a digital environment. In the academic paper The Role of Emotions for the Perceived Usefulness in Online Customer Reviews authors suggests that emotions impact the helpfulness ratings, i.e., the quality of online reviews as perceived by other customers. They found that, on average, the most prominent emotion dimensions that influence helpfulness ratings are trust, joy, and anticipation. Inspired by these findings, I decided to apply natural language processing techniques to analyze online customer reviews of a bestselling product on Amazon and try to detect those emotions using available lexicons. Final insights will show us whether trust, joy and anticipation can be identified in the reviews, thus improve helpfulness of reviews for potential customers.

What to expect in this article?

First, I will extract text via web-scrapping and form a corpus. Next, the text in the corpus will be pre-processed. Subsequently, from the pre-processed text will be stored in form of document-term-matrices or term-document matrices. Finally, an exploratory text analysis will be conducted and corresponding marketing implications pointed out.

Dictionaries for NLP

For this exercise I will use 3 different lexicons available for R. One of them is AFINN, a lexicon of words rated for valence between minus five (indicating negative valence) and plus five (indicating positive valence). Next, I will use NRC Emotion Lexicon, which consists of English words and their labels for eight basic emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive).

# Dictionaries ----
afinn <- get_sentiments("afinn")
bing <- get_sentiments("bing")
loughran <- get_sentiments("loughran")
nrc <- get_sentiment_dictionary('nrc', language = "english")

Data set

For our analysis, we will use text of 200 online customer reviews from Apple MacBook Pro (16-inch, 16GB RAM, 512GB Storage, 2.6GHz Intel Core i7) obtained in unpre-processed form:

 [1] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  Great product. Replacement for my other macbook that lasted 7 years and was still going strong besides the battery. Great upgrade\n\n  \n"                                                                                                                                             
 [2] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  Top notch until you don't use external monitor as well. Its common issue\n\n  \n"                                                                                                                                                                                                      
 [3] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  This is my first Mac and I love it. Apple is the BEST!\n\n  \n"                                                                                                                                                                                                                        
 [4] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  Basically, the laptop is a work of art. You know exactly what youre buying with an Apple product. I love that it comes with 16 GB of memory as default. The video card is very powerful. The touchpad is huge and works flawlessly. It is expensive, but worth every penny.\n\n  \n"   
 [5] "\n\n\n\n\n\n\n\n\n\n  \n    \n  A los dos dias de usarlo la pantalla se puso a rayas\n\n  \n  \n"                                                                                                                                                                                                                          
 [6] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  Product too heavy. Returned and have not received refund.\n\n  \n"                                                                                                                                                                                                                     
 [7] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  I love this laptop - the screen resolution is beautiful, the light up touch bar is super nice and an innovative touch. I have recently converted form a PC user to MAC after all the years that I have enjoyed other apple products such as Ipad, and I doubt I'd switch back.\n\n  \n"
 [8] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  It is well made, Liked it had two positions when opened rather than just one.  However, it would have been better if it had three reading positions when opened.\n\n  \n"                                                                                                              
 [9] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  Great laptop! I have been using it for months now and havent had any issues. Highly recommended\n\n  \n"                                                                                                                                                                               
[10] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  I opened the box and was like, Hello Lover  It's gorgeous.\n\n  \n"                                                                                                                                                                                                                    

Corpus cleaning

From the results above we could see that text contains unnecessary characters. Therefore, I will use some usual procedure to clean up the reviews’ text and make it more understandable.

For the purpose of this exercise and for efficiency reasons, we will use the volatile corpus, that stores the collection of documents in RAM memory. To create a volatile corpus, I need to pass reviews’ text in such a form that each review text is interpretated as a document.

# Creation of volatile corpus
review.corpus <- VCorpus(VectorSource(review$review_text))

We see that the volatile corpus contains as many documents as many online reviews we collected.

To undertake a custom transformation, I will use tm package and content_transformer() function. It takes a custom function as input, which defines what transformation needs to be done:

review.toSpace<- content_transformer(function (x , pattern ) gsub(pattern, " ", x))
review.corpus <- tm_map(review.corpus, review.toSpace, "/") # remove "/"
review.corpus <- tm_map(review.corpus, review.toSpace, "@") # remove "@"
review.corpus <- tm_map(review.corpus, review.toSpace, "\\|") # remove "\\|"
review.corpus <- tm_map(review.corpus, content_transformer(tolower)) # convert all capital letters to small
review.corpus <- tm_map(review.corpus, removeNumbers) # convert all capital letters to small
review.corpus <- tm_map(review.corpus, removeWords, stopwords("english")) # remove stop-words 
review.corpus <- tm_map(review.corpus, removePunctuation) # remove punctuation
review.corpus <- tm_map(review.corpus, stripWhitespace) # strip extra whitespace from a document

After cleaning the corpus, we can use document-term-matrix to store our cleaned corpus:

review.dtm <- DocumentTermMatrix(review.corpus)

However, document-term-matrix is not the most suitable to work with, because it stores review texts in rows and terms frequencies in columns. We will transform it with tidy function:

# Tidy up the document-term-matrix
review.tidy <- tidy(review.dtm)
review.tidy$count <-as.numeric(review.tidy$count) # Ensure correct class
colnames(review.tidy)[2]<- 'word' # change name of the column from "term" to "word"
review.tidy$document <- as.numeric(review.tidy$document) # Ensure correct class

Our tidy format has dimensions 6907 (the total number of terms) x 3 (document, term and count of the term in corresponding document):

dim(review.tidy) # Dimensions
[1] 6907    3
head(review.tidy)# Display first 6 rows

Visualisations of terms frequency

Bar charts with the most frequent terms

We would be interested in the most frequent words used in customer reviews. Sometimes just a glimpse of the most frequent words is sufficient to get some insights.

Here we see that word “love” and “great” appears among most frequent terms.

# Most frequent terms ----
review.tdm <- TermDocumentMatrix(review.corpus)
review.m <- as.data.frame.matrix(review.tdm)
review.v <- sort(rowSums(review.m),decreasing=TRUE)
word.names<-names(review.v)
df.review.v<-data.frame(review.v,word.names)
colnames(df.review.v)<-c("n","word")
p<-ggplot(data=df.review.v[1:20,], aes(x=reorder(word,n), y=n)) +
  geom_bar(stat="identity",fill="steelblue") + 
  coord_flip() + 
  ggtitle("20 most frequent words in customer reviews - MacBook Pro")+
  xlab("Count")+
  ylab("Word")+
  theme_test()
ggplotly(p)

NA

Wordcloud with the most frequent terms

Similarly to the bar chart with the most frequent words, we could use wordcloud as well. It displays words from the corpus and signalizes their frequency by displaying more frequent words bigger relative to those that appear less frequently in the corpus. In the wordcloud below you can see 200 most frequent words, where the minimum frequency was set to 1.

# Wordcloud
review.tdm <- TermDocumentMatrix(review.corpus)
review.m <- as.matrix(review.tdm)
review.v <- sort(rowSums(review.m),decreasing=TRUE)
review.d <- data.frame(word = names(review.v),freq=review.v)
set.seed(1234)
wordcloud(words = review.d$word, freq = review.d$freq, min.freq = 1,
          max.words=200, random.order=FALSE, rot.per=0.35,
          colors=brewer.pal(8, "Dark2"))

The most frequent terms indicating emotions

When it comes to anticipation, words such as “good”,“time”,“happy” or “powerful” indicates that this emotion can be identified among customer reviews. On the other hand, there are some words that could be a signal both for good and bad experience: “finally”,“money” or “wait”.

# Anticipation words----
nrc.anticipation <- subset(nrc, nrc$sentiment=="anticipation")
review.anticipation.words <- inner_join(review.tidy, nrc.anticipation)
review.anticipation.words <- count(review.anticipation.words, word)
review.anticipation.words <- review.anticipation.words[order(review.anticipation.words$n,decreasing = TRUE),]
p<-ggplot(data=review.anticipation.words[1:20,], aes(x=reorder(word,n), y=n)) +
  geom_bar(stat="identity",fill="orange") + 
  coord_flip() + 
  ggtitle("20 most frequent anticipation words in customer reviews")+
  xlab("Count")+
  ylab("Word")+
  theme_minimal()
ggplotly(p)

Similarly to anticipation, now we observe a list of top 20 words that indicate trust. It reveals new quite frequent term in the corpus: “recommend”.

# Trust words----
nrc.trust <- subset(nrc, nrc$sentiment=="trust")
review.trust.words <- inner_join(review.tidy, nrc.trust)
review.trust.words <- count(review.trust.words, word)
review.trust.words <- review.trust.words[order(review.trust.words$n,decreasing = TRUE),]
p<-ggplot(data=review.trust.words[1:20,], aes(x=reorder(word,n), y=n)) +
  geom_bar(stat="identity",fill="royalblue1") + 
  coord_flip() + 
  ggtitle("20 most frequent trust words in customer reviews")+
  xlab("Count")+
  ylab("Word")+
  theme_minimal()
ggplotly(p)

Although at the bottom of the list, “The top 20 list” of joy words displays some additional words that we did not observe previously such as “beautiful”,“gorgeous”,“wonderful”,“improvement”,“excellent”.

# Joy words ----
nrc.joy <- subset(nrc, nrc$sentiment=="joy")
review.joy.words <- inner_join(review.tidy, nrc.joy)
review.joy.words <- count(review.joy.words, word)
review.joy.words <- review.joy.words[order(review.joy.words$n,decreasing = TRUE),]
p<-ggplot(data=review.joy.words[1:20,], aes(x=reorder(word,n), y=n)) +
  geom_bar(stat="identity",fill="darkorange1") + 
  coord_flip() + 
  ggtitle("20 most frequent trust words in customer reviews")+
  xlab("Count")+
  ylab("Word")+
  theme_minimal()
ggplotly(p)

Sentiment analysis

Polarity timeline

One usual way to compare and quantify emotions in text is via polarity. We simply count number of unique words in each document (=review) labelled as negative and deduct from the count of unique positive words. For instance, the first review contains 2 unique positive words (“great” and “strong”) and none negative unique words. Therefore, its polarity score is 2.

This polarity timeline suggests very important implication: the reviews’ sentiment is moving above the 0, bearly going even below +2, giving an indication that this product continuously meet customers’ expectations. That is a good signal to believe that customers are rather satisfied with the product.

# Polarity timeline ----
review.sentiment <- inner_join(review.tidy, bing)
review.sentiment <- count(review.sentiment, sentiment, index=document)
review.sentiment <- spread(review.sentiment, sentiment, n, fill=0)
review.sentiment$polarity <- review.sentiment$positive - review.sentiment$negative
review.sentiment$pos <- ifelse(review.sentiment$polarity >=0, "Positive", "Negative")
p<-ggplot(review.sentiment, aes(x=index, y=polarity, fill=pos))+geom_bar(stat="identity", position="identity", width=1)+theme_gdocs()+ggtitle(label="Polarity timeline")
ggplotly(p)


# Smooth curve
review.smooth <- ggplot(review.sentiment, aes(index, polarity))
p<-review.smooth + stat_smooth() + theme_gdocs() + ggtitle("Polarity timeline - smooth")
ggplotly(p)

NA

In the polarity graph at index 81 we identify a review with sentiment score of even 34! This seems to be a thrilled customer every brand loves! Let us take closer look:

review.sentiment <- inner_join(review.tidy, bing)
doc_81<-filter(review.sentiment, document=="81")
head(doc_81[order(doc_81$count,decreasing = T),])

Finally, it certainly pays off to check the actual review:

# Outlier in polarity score
review$review_text[81]
[1] "\n\n\n\n\n\n\n\n\n\n  \n  \n    \n  If you've been waiting for Apple to wake up and address the concerns raised by the Apple community -- your wait is now over. This is the MacBook Pro we've all been wanting for years. This review is for the higher end model, stock.The good.- Keyboard. It's fantastic. The key travel has been extended to 1mm, which is about half of the original keyboard found on the 2015 and prior model years. It feels just as good to type on because apple improved the tactile feedback. The keys sort of spring back. The keyboard is quiet and very comfortable to type on. The keys are slightly smaller but do not make typing on them any more difficult. The directional arrows are now properly setup and the physical Esc key is back, making it a breeze to flip through open applications.- Screen. The difference in size is subtle but noticeable. It's technically even more dense, but just barely. You won't notice much difference from recent MacBook Pros but true to Apple, the display is absolutely gorgeous. The 0.6 inch difference in size retains the same logical resolution, so everything should look just slightly bigger which I welcome.- Processor. The base model has the same chipset as the 2019 15.4\" model. The performance, however, is about 10% better due to significantly improved airflow and larger heatsinks. The issue of heat related throttling has been largely addressed. On the upper model, the machine now comes with 8 cores and serious performance bump. There is literally nothing you can throw at this MacBook that it won't handle with breeze.- Graphics. The traditional setup is still here. You have the onboard Intel chipset, which operates when the demand for visual performance is low. You also have a discrete AMD chip which delivers significant improvement over the previous generation. In fact, the base model delivers performance in excess of the upper spec Vega chipset from last year. The leap is extraordinary. As before, the system will automatically choose which graphics card to use depending on demand.- Sound. In one world, amazing. Imagine Apple took a HomePod and flattened it to fit it in the housing of the MacBook Pro. That is essentially the experience. Bass is pronounced and crisp and treble is sharp. The sound is rich and room-filling. There are six speakers instead of two in the last generation.-Microphone. Major improvements with three mics instead of one in the previous generation. I use the MacBook to make calls using an iPhone and the sound on the other end is clear and free from background noice. I've been told it sounds a lot better than before, but that is of course subjective. On paper, you're getting better noise reduction and improved sound fidelity.- Touch Bar. Controversial in the past, I think it may now be the \"happy medium\" between physical keys and the useful Touch Bar which adapts to the content on the screen. The Esc key is back and on the right hand side you'll find Touch ID and power button.- SSD. You'll love the fact that now base starting size of the SSD has been doubled on both the entry model and the upper model with 512GB and 1TB respectively.- Gaming. This has to be mentioned. The graphics card offers incredible leap in performance. Modern games that would get 14-16FPS on high setting now perform at 35-40FPS with ease. Same settings. Same games. Huge improvement. It's now possible to play AAA games on the MacBook Pro with reasonable performance and high visual settings.- Productivity. This machine is a beast. I use the full Affinity suite and do some limited video editing. In addition, I have multiple productivity programs open, over a dozen Safari tabs, two email clients, and dozens of other apps, such as CRM, task managers, notes, etc. Everything runs smoothly.- Value. Yes, value. This expensive laptop brings the best value in the lineup of the 15.\"4 (now 16\") offering to date. If you carefully look at the costs of truly compatible Windows offerings, you'll find the MacBook Pro to be competitively priced.The Bad- Weight. The machine is slightly heavier but I welcome the added bulky. Finally Apple went with functionality over its obsession with thiner and lighter hardware, giving us a machine with proper thermal management, proper keyboard and more. But if you plan on taking it with you places, you'll feel the extra bulk.- BTO Updates. The cost of BTO options is still quite high, with the noted exception of the 8GB option on the GPU. You're still paying a significant premium for each incremental upgrade over the base model.To sum up -- This is the best MacBook Pro in many years. It's a well-rounded, powerful machine that brings about incredible performance and value. I would highly recommend it to any Pro.If you're upgrading from the 13\" model and wonder how much more real estate you can expect, see attached side-by-side comparison photo.Update 3/15/20 -- Absolutely a beast of a machine. I love it. It has been pretty much perfect and its performance continues to surprise me. I have the top-speced model and it smokes pretty much everything I've used to date. In fact, it will perform on par with the new Mac Pro base configuration. Don't believe me? Google it. This is by far the best MacBook Pro to date and an amazing value. Well worth the price of admission if you need it.\n\n  \n"

It seems that our assumption was correct! The customer was definitely thrilled! This is a nice example how you can identify and take closer look at reviews that stand out based on its polarity score.

Analysis on sentence-level

Text analysis provides freedom to choose level of observation. So far, we explored words and their frequencies, we explored customer reviews and quantified their sentiment in two dimensions (positive and negative). Next, we will approach the task of identifying the most negative and positive reviews by organizing text by sentences. By doing so, we will directly access those sentences whose average sentiment stand out.

# Calculating the average sentiment
review.highlighted<-review$review_text%>%
  get_sentences() %>%
  sentiment_by()
head(review.highlighted)
# Preparing data
review.score <- subset(review.highlighted, select = c("ave_sentiment","element_id"))
review.worst <- review.score[order(review.score$ave_sentiment,decreasing = FALSE),]
review.worst<-review.worst$element_id[1:10]
review.best <- review.score[order(review.score$ave_sentiment, decreasing = TRUE),]
review.best <- review.best$element_id[1:10]
sentences<-review$review_text %>% get_sentences()
sentences<-as.matrix(sentences)

And here we have “the worst 10 sentences” from customer reviews;

# 10 worst sentences
sentences[review.worst]
 [1] "The screen is nice but has ridiculous ghosting."                                                                                                 
 [2] "Very disappointed."                                                                                                                              
 [3] "Force cancelling woofers keep the annoying vibration at louder playback at bay."                                                                 
 [4] "I am very disappointed!"                                                                                                                         
 [5] "Having ports would have been nice (especially seeing how much space would be available for ports) but unfortunately, that is how Apple operates."
 [6] "This is a Finder issue, as apps launch but don't leap to the top."                                                                               
 [7] "Im glad I didnt have important files to transfer or I would have been upset."                                                                    
 [8] "I guess I got a lemon, which is disappointing considering the high cost."                                                                        
 [9] "Small issue: fan comes up too often ( I do application development with it)."                                                                    
[10] "Very suspicious batch."                                                                                                                          

Despite the fact that positive sentiment prevails, we see that there are certain problems associated with MacBook laptop. Issues with screen, problems with woofers, disappointment that there are no ports, unsatisfying value-price ratio.

# 10 most positive sentences
sentences[review.best]
 [1] "Yes its considerably big and heavy compared to others, but Honestly that just feels like better quality to me"                           
 [2] "Quality is very good."                                                                                                                   
 [3] "Extremely pleased with my NEW MacBook Pro!"                                                                                              
 [4] "This MacBook Pro 16\" is outstanding and much much better than the previous generation."                                                 
 [5] "Overall, this new MacBook is rather pricey, but there's a reason it is called \"Pro\" because it is designed for professional."          
 [6] "Very fast!"                                                                                                                              
 [7] "Very fast, great screen, fantastic keyboard and wonderful speakers."                                                                     
 [8] "If you're upgrading from the 13\" model and wonder how much more real estate you can expect, see attached side-by-side comparison photo."
 [9] "Well made computerBest specsNo much innovation from the 15 MacBook Pro that I Have."                                                     
[10] "I absolutely love this new Mac!"                                                                                                         

If we take a look at “10 most positive sentences” from customer reviews, we would find a similar evidence as we obtained with polarity score. However, by reading those sentences a reader can have better feeling what the reviewer is actually satisfied or unsatisfied with. Here we see that some people admire the speed for instance.

What are the most emotional reviews?

Package sentimentr provides nice function emotion() which uses a dictionary to find emotion words and then compute the rate per sentence. The final emotion score ranges between 0 (no emotion used) and 1 (all words used were emotional).

# Extract emotions terms
reviews.emotion <- review$review_text %>% get_sentences() %>% emotion()

# Top 50 sentences with the highest emotion score 
top_emotional_sentences <- unique(reviews.emotion[order(reviews.emotion$emotion,decreasing = TRUE),]$element_id[1:50])

# The most emotional reviews
sentences[top_emotional_sentences,]
 [1] "Frustrated!"          "PERFECT!!!!"          "Finally!"            
 [4] "Hot damn."            "Hot."                 "Damn."               
 [7] "LOVE LOVE LOVE it!"   "The Bad- Weight."     "Highly recommend."   
[10] "Highly recommend."    "Outstanding laptop."  "Excellent computer"  
[13] "Loving it!"           "Good computer."       "good product"        
[16] "Huge improvement."    "So disappointed....." "Very disappointed."  
[19] "so disappointed."     "Good keyboard."       "Ridiculously good."  

We can see that identified sentences very clearly reflect emotions that customers expressed. It seems that intensity of emotions is high in both positive and negative direction.

Finally, we can plot detected emotions in order to get a bit more clear insight in emotional structure detected in the reviews:

# Plot of emotion
plot(reviews.emotion,
     transformation.function = syuzhet::get_dct_transform,
     drop.unused.emotions = TRUE, facet = TRUE)

This plot depicts emotional inclination of the reviews. Curves indicating emotional propensity of trust, joy and anticipation suggest strong inclination towards mentioned emotions. In line with the academic paper The Role of Emotions for the Perceived Usefulness in Online Customer Reviews, we have found an evidence that online reviews analyzed encode emotions contributing to the higher helpfulness rating, i.e. quality of reviews.

In the end, it is important to mention that the role and influence of emotions defined by Plutchik on the quality of reviews differ across different product categories.

LS0tDQp0aXRsZTogIkVtb3Rpb25zX0luX09ubGluZV9DdXN0b21lcl9SZXZpZXdzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KIyAoUEFSVCkgTXkgd29yayB7LX0NCg0KIyBFbW90aW9ucyBJbiBPbmxpbmUgQ3VzdG9tZXIgUmV2aWV3cyANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3I9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciIpDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjUwJSIsZmlnLmNhcD0iRm90byB2b24gQW5kcmVhIFBpYWNxdWFkaW8gdm9uIFBleGVscyJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiR3JhcGhpY3MvZW1vdGlvbnMuanBnIikNCmBgYA0KDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiANCg0KQ29uc3VtZXJzIHVzdWFsbHkgc2VlayBxdWFsaXR5IGFuZCByZWxldmFudCBpbmZvcm1hdGlvbiB3aGVuIGJ1eWluZyBuZXcgcHJvZHVjdHMuIFdpdGggdGhlIGV4cGFuc2lvbiBhbmQgYXZhaWxhYmlsaXR5IG9mIHRoZSBJbnRlcm5ldCwgb25saW5lIGNvbnN1bWVyIHJldmlld3MgaGF2ZSBiZWNvbWUgYSB2YWx1YWJsZSByZXNvdXJjZSB0byBsb29rIGF0LiBTZXZlcmFsIHN0dWRpZXMgdHJpZWQgdG8gZGVteXN0aWZ5IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHByb2R1Y3Qgc2FsZXMgYW5kIG9ubGluZSBjdXN0b21lciByZXZpZXdzLiBPbiB0aGUgb25lIGhhbmQsIHNvbWUgb2YgdGhlbSwgc3VjaCBhcyBbU2VuZWNhbCBhbmQgTmFudGVsICgyMDA0KV0oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wdWJsaWNhdGlvbi8yMjI1MTkxMTJfVGhlX0luZmx1ZW5jZV9vZl9PbmxpbmVfUHJvZHVjdF9SZWNvbW1lbmRhdGlvbnNfb25fQ29uc3VtZXJzJ09ubGluZV9DaG9pY2VzKSwgc3VnZ2VzdCB0aGF0IHBhcnRpY2lwYW50cyB3aG8gY29uc3VsdGVkIHByb2R1Y3QgcmVjb21tZW5kYXRpb25zIHNlbGVjdGVkIHRoZXNlIHByb2R1Y3RzIHR3aWNlIGFzIG9mdGVuIGFzIHRob3NlIHdobyBkaWQgbm90IGNvbnN1bHQgcmVjb21tZW5kYXRpb25zLiBPbiB0aGUgb3RoZXIgaGFuZCwgW1poYW5nIGFuZCBEZWxsYXJvY2FzICgyMDA2KV0oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wdWJsaWNhdGlvbi8yMjc2MDA5NTBfRXhwbG9yaW5nX3RoZV9WYWx1ZV9vZl9PbmxpbmVfUHJvZHVjdF9SZXZpZXdzX2luX0ZvcmVjYXN0aW5nX1NhbGVzX1RoZV9DYXNlX29mX01vdGlvbl9QaWN0dXJlcykgZmluZCB0aGF0IG9ubGluZSByZXZpZXdzIGFuZCBkbyBub3QgaW5mbHVlbmNlIHNhbGVzIGFuZCBzZXJ2ZSBzb2xlbHkgYXMgcHJlZGljdGlvbi4NCg0KQmV0d2VlbiB0aGVzZSB0d28gb3BpbmlvbiBmcm9udHMsIG9uZSB0aGluZyBpcyBjZXJ0YWluOiBib3RoIHNpZGVzIGFpbSB0byBmaW5kIG91dCBob3cgY29uc3VtZXJzIHBlcmNlaXZlIGFuZCBwcm9jZXNzIHdvcmQtb2YtbW91dGggaW4gYSBkaWdpdGFsIGVudmlyb25tZW50LiBJbiB0aGUgYWNhZGVtaWMgcGFwZXIgW1RoZSBSb2xlIG9mIEVtb3Rpb25zIGZvciB0aGUgUGVyY2VpdmVkIFVzZWZ1bG5lc3MgaW4gT25saW5lIEN1c3RvbWVyIFJldmlld3NdKCNodHRwczovL3d3dy5qc3Rvci5vcmcvc3RhYmxlL3BkZi8yMDYxOTA5NS5wZGYNCikgYXV0aG9ycyBzdWdnZXN0cyB0aGF0IGVtb3Rpb25zIGltcGFjdCB0aGUgaGVscGZ1bG5lc3MgcmF0aW5ncywgaS5lLiwgdGhlIHF1YWxpdHkgb2Ygb25saW5lIHJldmlld3MgYXMgcGVyY2VpdmVkIGJ5IG90aGVyIGN1c3RvbWVycy4gVGhleSBmb3VuZCB0aGF0LCBvbiBhdmVyYWdlLCB0aGUgbW9zdCBwcm9taW5lbnQgZW1vdGlvbiBkaW1lbnNpb25zIHRoYXQgaW5mbHVlbmNlIGhlbHBmdWxuZXNzIHJhdGluZ3MgYXJlICoqdHJ1c3QsIGpveSwgYW5kIGFudGljaXBhdGlvbioqLiBJbnNwaXJlZCBieSB0aGVzZSBmaW5kaW5ncywgSSBkZWNpZGVkIHRvIGFwcGx5IG5hdHVyYWwgbGFuZ3VhZ2UgcHJvY2Vzc2luZyB0ZWNobmlxdWVzIHRvIGFuYWx5emUgb25saW5lIGN1c3RvbWVyIHJldmlld3Mgb2YgYSBiZXN0c2VsbGluZyBwcm9kdWN0IG9uIEFtYXpvbiBhbmQgdHJ5IHRvIGRldGVjdCB0aG9zZSBlbW90aW9ucyB1c2luZyBhdmFpbGFibGUgbGV4aWNvbnMuIEZpbmFsIGluc2lnaHRzIHdpbGwgc2hvdyB1cyB3aGV0aGVyIHRydXN0LCBqb3kgYW5kIGFudGljaXBhdGlvbiBjYW4gYmUgaWRlbnRpZmllZCBpbiB0aGUgcmV2aWV3cywgdGh1cyBpbXByb3ZlIGhlbHBmdWxuZXNzIG9mIHJldmlld3MgZm9yIHBvdGVudGlhbCBjdXN0b21lcnMuDQoNCjwvZGl2Pg0KDQojIyBXaGF0IHRvIGV4cGVjdCBpbiB0aGlzIGFydGljbGU/IHstfQ0KDQpGaXJzdCwgSSB3aWxsIGV4dHJhY3QgdGV4dCB2aWEgd2ViLXNjcmFwcGluZyBhbmQgZm9ybSBhIGNvcnB1cy4gTmV4dCwgdGhlIHRleHQgaW4gdGhlIGNvcnB1cyB3aWxsIGJlIHByZS1wcm9jZXNzZWQuIFN1YnNlcXVlbnRseSwgZnJvbSB0aGUgcHJlLXByb2Nlc3NlZCB0ZXh0IHdpbGwgYmUgc3RvcmVkIGluIGZvcm0gb2YgZG9jdW1lbnQtdGVybS1tYXRyaWNlcyBvciB0ZXJtLWRvY3VtZW50IG1hdHJpY2VzLiBGaW5hbGx5LCBhbiBleHBsb3JhdG9yeSB0ZXh0IGFuYWx5c2lzIHdpbGwgYmUgY29uZHVjdGVkIGFuZCBjb3JyZXNwb25kaW5nIG1hcmtldGluZyBpbXBsaWNhdGlvbnMgcG9pbnRlZCBvdXQuDQoNCmBgYHtyLGVjaG89RkFMU0V9DQojIFBhY2thZ2VzIC0tLS0NCmxpYnJhcnkoc2VudGltZW50cikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHRleHRkYXRhKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoeG1sMikNCmxpYnJhcnkocnZlc3QpDQpsaWJyYXJ5KHdvcmRjbG91ZCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShOTFApDQpsaWJyYXJ5KHRtKQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KbGlicmFyeShyZW1vdGVzKQ0KbGlicmFyeShqYW5lYXVzdGVucikNCmxpYnJhcnkocWRhcCkNCmxpYnJhcnkoc3l1emhldCkNCmxpYnJhcnkoc2ptaXNjKQ0KbGlicmFyeSh0b3BpY21vZGVscykNCmBgYA0KDQojIyBEaWN0aW9uYXJpZXMgZm9yIE5MUA0KDQpGb3IgdGhpcyBleGVyY2lzZSBJIHdpbGwgdXNlIDMgZGlmZmVyZW50IGxleGljb25zIGF2YWlsYWJsZSBmb3IgUi4NCk9uZSBvZiB0aGVtIGlzIEFGSU5OLCBhIGxleGljb24gb2Ygd29yZHMgcmF0ZWQgZm9yIHZhbGVuY2UgYmV0d2VlbiBtaW51cyBmaXZlIChpbmRpY2F0aW5nIG5lZ2F0aXZlIHZhbGVuY2UpIGFuZCBwbHVzIGZpdmUgKGluZGljYXRpbmcgcG9zaXRpdmUgdmFsZW5jZSkuIE5leHQsIEkgd2lsbCB1c2UgTlJDIEVtb3Rpb24gTGV4aWNvbiwgd2hpY2ggY29uc2lzdHMgb2YgRW5nbGlzaCB3b3JkcyBhbmQgdGhlaXIgbGFiZWxzIGZvciBlaWdodCBiYXNpYyBlbW90aW9ucyAoYW5nZXIsIGZlYXIsIGFudGljaXBhdGlvbiwgdHJ1c3QsIHN1cnByaXNlLCBzYWRuZXNzLCBqb3ksIGFuZCBkaXNndXN0KSBhbmQgdHdvIHNlbnRpbWVudHMgKG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSkuDQoNCmBgYHtyfQ0KIyBEaWN0aW9uYXJpZXMgLS0tLQ0KYWZpbm4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCmJpbmcgPC0gZ2V0X3NlbnRpbWVudHMoImJpbmciKQ0KbG91Z2hyYW4gPC0gZ2V0X3NlbnRpbWVudHMoImxvdWdocmFuIikNCm5yYyA8LSBnZXRfc2VudGltZW50X2RpY3Rpb25hcnkoJ25yYycsIGxhbmd1YWdlID0gImVuZ2xpc2giKQ0KYGBgDQoNCiMjIERhdGEgc2V0DQoNCmBgYHtyLGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9DQojIFdlYiBzY3JhcGluZyAtLS0tDQpzY3JhcGluZyA8LSBmdW5jdGlvbihBU0lOLCBwYWdlX251bSl7DQogIA0KICB1cmxfcmV2aWV3cyA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmFtYXpvbi5jb20vcHJvZHVjdC1yZXZpZXdzLyIsQVNJTiwiLz9wYWdlTnVtYmVyPSIscGFnZV9udW0pDQogIA0KICBkb2MgPC0gcmVhZF9odG1sKHVybF9yZXZpZXdzKQ0KICANCiAgIyBSZXZpZXcgVGl0bGUNCnJldmlld190aXRsZSA8LSBkb2MgJT4lIA0KICAgIGh0bWxfbm9kZXMoIltjbGFzcz0nYS1zaXplLWJhc2UgYS1saW5rLW5vcm1hbCByZXZpZXctdGl0bGUgYS1jb2xvci1iYXNlIHJldmlldy10aXRsZS1jb250ZW50IGEtdGV4dC1ib2xkJ10iKSAlPiUNCiAgICBodG1sX3RleHQoKQ0KICANCiAgIyBSZXZpZXcgVGV4dA0KcmV2aWV3X3RleHQgPC0gZG9jICU+JSANCiAgICBodG1sX25vZGVzKCJbY2xhc3M9J2Etc2l6ZS1iYXNlIHJldmlldy10ZXh0IHJldmlldy10ZXh0LWNvbnRlbnQnXSIpICU+JQ0KICAgIGh0bWxfdGV4dCgpDQogIA0KICAjIE51bWJlciBvZiBzdGFycyBpbiByZXZpZXcNCnJldmlld19zdGFyIDwtICBkb2MgJT4lDQogICAgaHRtbF9ub2RlcygiW2RhdGEtaG9vaz0ncmV2aWV3LXN0YXItcmF0aW5nJ10iKSAlPiUNCiAgICBodG1sX3RleHQoKQ0KICANCiAgIyBSZXR1cm4gYSB0aWJibGUNCiAgdGliYmxlKHJldmlld190aXRsZSwNCiAgICAgICAgIHJldmlld190ZXh0LA0KICAgICAgICAgcmV2aWV3X3N0YXIsDQogICAgICAgICBwYWdlID0gcGFnZV9udW0pICU+JSByZXR1cm4oKQ0KfQ0KDQojc2NyYXBpbmcoQVNJTiA9ICJCMDg3TFcyS0ZHIiwgcGFnZV9udW0gPSA1KQ0KDQojLS0tLQ0KQVNJTiA8LSAiQjA4MUZaVjQ1SCIgIyBOZXcgQXBwbGUgTWFjQm9vayBQcm8gKDE2LWluY2gsIDE2R0IgUkFNLCA1MTJHQiBTdG9yYWdlLCAyLjZHSHogSW50ZWwgQ29yZSBpNykgLSBTcGFjZSBHcmF5DQpwYWdlX3JhbmdlIDwtIDE6MjAgIyBMZXQncyBzYXkgd2Ugd2FudCB0byBzY3JhcGUgcGFnZXMgMSB0byAxMA0KDQojIENyZWF0ZSBhIHRhYmxlIHRoYXQgc2NyYW1ibGVzIHBhZ2UgbnVtYmVycyB1c2luZyBgc2FtcGxlKClgDQojIEZvciByYW5kb21pc2luZyBwYWdlIHJlYWRzIQ0KbWF0Y2hfa2V5IDwtIHRpYmJsZShuID0gcGFnZV9yYW5nZSwNCiAgICAgICAgICAgICAgICAgICAga2V5ID0gc2FtcGxlKHBhZ2VfcmFuZ2UsbGVuZ3RoKHBhZ2VfcmFuZ2UpKSkNCg0KbGFwcGx5KHBhZ2VfcmFuZ2UsIGZ1bmN0aW9uKGkpew0KICBqIDwtIG1hdGNoX2tleVttYXRjaF9rZXkkbj09aSxdJGtleQ0KICANCiAgbWVzc2FnZSgiR2V0dGluZyBwYWdlICIsaSwgIiBvZiAiLGxlbmd0aChwYWdlX3JhbmdlKSwgIjsgQWN0dWFsOiBwYWdlICIsaikgIyBQcm9ncmVzcyBiYXINCiAgDQogIFN5cy5zbGVlcCgzKSAjIFRha2UgYSB0aHJlZSBzZWNvbmQgYnJlYWsNCiAgDQogIGlmKChpICUlIDMpID09IDApeyAjIEFmdGVyIGV2ZXJ5IHRocmVlIHNjcmFwZXMuLi4gdGFrZSBhbm90aGVyIHR3byBzZWNvbmQgYnJlYWsNCiAgICANCiAgICBtZXNzYWdlKCJUYWtpbmcgYSBicmVhay4uLiIpICMgUHJpbnRzIGEgJ3Rha2luZyBhIGJyZWFrJyBtZXNzYWdlIG9uIHlvdXIgY29uc29sZQ0KICAgIA0KICAgIFN5cy5zbGVlcCgyKSAjIFRha2UgYW4gYWRkaXRpb25hbCB0d28gc2Vjb25kIGJyZWFrDQogIH0NCiAgc2NyYXBpbmcoQVNJTiA9IEFTSU4sIHBhZ2VfbnVtID0gaikgIyBTY3JhcGUNCn0pIC0+IG91dHB1dF9saXN0DQpgYGANCg0KDQpGb3Igb3VyIGFuYWx5c2lzLCB3ZSB3aWxsIHVzZSB0ZXh0IG9mIDIwMCBvbmxpbmUgY3VzdG9tZXIgcmV2aWV3cyBmcm9tICpBcHBsZSBNYWNCb29rIFBybyAoMTYtaW5jaCwgMTZHQiBSQU0sIDUxMkdCIFN0b3JhZ2UsIDIuNkdIeiBJbnRlbCBDb3JlIGk3KSogb2J0YWluZWQgaW4gdW5wcmUtcHJvY2Vzc2VkIGZvcm06DQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KIyBMb2FkIGluIGRhdGENCm91dHB1dF9saXN0IDwtIHJlYWRSRFMoImRhdGEvTWFjQm9vay5yZHMiKQ0KIyBDb21waWxlIG9ubGluZSBjdXN0b21lciByZXZpZXdzIHdpdGggY29ycmVzcG9uZGluZyBwYWdlDQpyZXZpZXcgPC0gYmluZF9yb3dzKG91dHB1dF9saXN0LCAuaWQgPSAicGFnZSIpDQojIFRyYW5zZm9ybSB0aGUgdGV4dCB0byBVVEYtOCANCnJldmlldyRyZXZpZXdfdGV4dCA8LSBpY29udihyZXZpZXckcmV2aWV3X3RleHQsICd1dGYtOCcsICdhc2NpaScsIHN1Yj0nJykNCiMgT2JzZXJ2ZSB0aGUgdGV4dA0KaGVhZChyZXZpZXckcmV2aWV3X3RleHQsMTApDQpgYGANCg0KIyMgQ29ycHVzIGNsZWFuaW5nDQoNCkZyb20gdGhlIHJlc3VsdHMgYWJvdmUgd2UgY291bGQgc2VlIHRoYXQgdGV4dCBjb250YWlucyB1bm5lY2Vzc2FyeSBjaGFyYWN0ZXJzLiBUaGVyZWZvcmUsIEkgd2lsbCB1c2Ugc29tZSB1c3VhbCBwcm9jZWR1cmUgdG8gY2xlYW4gdXAgdGhlIHJldmlld3MnIHRleHQgYW5kIG1ha2UgaXQgbW9yZSB1bmRlcnN0YW5kYWJsZS4NCg0KRm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgZXhlcmNpc2UgYW5kIGZvciBlZmZpY2llbmN5IHJlYXNvbnMsIHdlIHdpbGwgdXNlIHRoZSB2b2xhdGlsZSBjb3JwdXMsIHRoYXQgc3RvcmVzIHRoZSBjb2xsZWN0aW9uIG9mIGRvY3VtZW50cyBpbiBSQU0gbWVtb3J5LiBUbyBjcmVhdGUgYSB2b2xhdGlsZSBjb3JwdXMsIEkgbmVlZCB0byBwYXNzIHJldmlld3MnIHRleHQgaW4gc3VjaCBhIGZvcm0gdGhhdCBlYWNoIHJldmlldyB0ZXh0IGlzIGludGVycHJldGF0ZWQgYXMgYSBkb2N1bWVudC4NCg0KYGBge3J9DQojIENyZWF0aW9uIG9mIHZvbGF0aWxlIGNvcnB1cw0KcmV2aWV3LmNvcnB1cyA8LSBWQ29ycHVzKFZlY3RvclNvdXJjZShyZXZpZXckcmV2aWV3X3RleHQpKQ0KYGBgDQpXZSBzZWUgdGhhdCB0aGUgdm9sYXRpbGUgY29ycHVzIGNvbnRhaW5zIGFzIG1hbnkgZG9jdW1lbnRzIGFzIG1hbnkgb25saW5lIHJldmlld3Mgd2UgY29sbGVjdGVkLg0KDQpUbyB1bmRlcnRha2UgYSBjdXN0b20gdHJhbnNmb3JtYXRpb24sIEkgd2lsbCB1c2UgYHRtYCBwYWNrYWdlIGFuZCBgY29udGVudF90cmFuc2Zvcm1lcigpYCBmdW5jdGlvbi4NCkl0IHRha2VzIGEgY3VzdG9tIGZ1bmN0aW9uIGFzIGlucHV0LCB3aGljaCBkZWZpbmVzIHdoYXQgdHJhbnNmb3JtYXRpb24gbmVlZHMgdG8gYmUgZG9uZTogDQoNCmBgYHtyfQ0KcmV2aWV3LnRvU3BhY2U8LSBjb250ZW50X3RyYW5zZm9ybWVyKGZ1bmN0aW9uICh4ICwgcGF0dGVybiApIGdzdWIocGF0dGVybiwgIiAiLCB4KSkNCnJldmlldy5jb3JwdXMgPC0gdG1fbWFwKHJldmlldy5jb3JwdXMsIHJldmlldy50b1NwYWNlLCAiLyIpICMgcmVtb3ZlICIvIg0KcmV2aWV3LmNvcnB1cyA8LSB0bV9tYXAocmV2aWV3LmNvcnB1cywgcmV2aWV3LnRvU3BhY2UsICJAIikgIyByZW1vdmUgIkAiDQpyZXZpZXcuY29ycHVzIDwtIHRtX21hcChyZXZpZXcuY29ycHVzLCByZXZpZXcudG9TcGFjZSwgIlxcfCIpICMgcmVtb3ZlICJcXHwiDQpyZXZpZXcuY29ycHVzIDwtIHRtX21hcChyZXZpZXcuY29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAjIGNvbnZlcnQgYWxsIGNhcGl0YWwgbGV0dGVycyB0byBzbWFsbA0KcmV2aWV3LmNvcnB1cyA8LSB0bV9tYXAocmV2aWV3LmNvcnB1cywgcmVtb3ZlTnVtYmVycykgIyBjb252ZXJ0IGFsbCBjYXBpdGFsIGxldHRlcnMgdG8gc21hbGwNCnJldmlldy5jb3JwdXMgPC0gdG1fbWFwKHJldmlldy5jb3JwdXMsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgIyByZW1vdmUgc3RvcC13b3JkcyANCnJldmlldy5jb3JwdXMgPC0gdG1fbWFwKHJldmlldy5jb3JwdXMsIHJlbW92ZVB1bmN0dWF0aW9uKSAjIHJlbW92ZSBwdW5jdHVhdGlvbg0KcmV2aWV3LmNvcnB1cyA8LSB0bV9tYXAocmV2aWV3LmNvcnB1cywgc3RyaXBXaGl0ZXNwYWNlKSAjIHN0cmlwIGV4dHJhIHdoaXRlc3BhY2UgZnJvbSBhIGRvY3VtZW50DQpgYGANCg0KQWZ0ZXIgY2xlYW5pbmcgdGhlIGNvcnB1cywgd2UgY2FuIHVzZSBkb2N1bWVudC10ZXJtLW1hdHJpeCB0byBzdG9yZSBvdXIgY2xlYW5lZCBjb3JwdXM6DQoNCmBgYHtyfQ0KcmV2aWV3LmR0bSA8LSBEb2N1bWVudFRlcm1NYXRyaXgocmV2aWV3LmNvcnB1cykNCmBgYA0KDQpIb3dldmVyLCBkb2N1bWVudC10ZXJtLW1hdHJpeCBpcyBub3QgdGhlIG1vc3Qgc3VpdGFibGUgdG8gd29yayB3aXRoLCBiZWNhdXNlIGl0IHN0b3JlcyByZXZpZXcgdGV4dHMgaW4gcm93cyBhbmQgdGVybXMgZnJlcXVlbmNpZXMgaW4gY29sdW1ucy4gV2Ugd2lsbCB0cmFuc2Zvcm0gaXQgd2l0aCBgdGlkeWAgZnVuY3Rpb246DQoNCmBgYHtyfQ0KIyBUaWR5IHVwIHRoZSBkb2N1bWVudC10ZXJtLW1hdHJpeA0KcmV2aWV3LnRpZHkgPC0gdGlkeShyZXZpZXcuZHRtKQ0KcmV2aWV3LnRpZHkkY291bnQgPC1hcy5udW1lcmljKHJldmlldy50aWR5JGNvdW50KSAjIEVuc3VyZSBjb3JyZWN0IGNsYXNzDQpjb2xuYW1lcyhyZXZpZXcudGlkeSlbMl08LSAnd29yZCcgIyBjaGFuZ2UgbmFtZSBvZiB0aGUgY29sdW1uIGZyb20gInRlcm0iIHRvICJ3b3JkIg0KcmV2aWV3LnRpZHkkZG9jdW1lbnQgPC0gYXMubnVtZXJpYyhyZXZpZXcudGlkeSRkb2N1bWVudCkgIyBFbnN1cmUgY29ycmVjdCBjbGFzcw0KYGBgDQoNCk91ciB0aWR5IGZvcm1hdCBoYXMgZGltZW5zaW9ucyA2OTA3ICh0aGUgdG90YWwgbnVtYmVyIG9mIHRlcm1zKSB4IDMgKGRvY3VtZW50LCB0ZXJtIGFuZCBjb3VudCBvZiB0aGUgdGVybSBpbiBjb3JyZXNwb25kaW5nIGRvY3VtZW50KToNCg0KYGBge3J9DQpkaW0ocmV2aWV3LnRpZHkpICMgRGltZW5zaW9ucw0KaGVhZChyZXZpZXcudGlkeSkjIERpc3BsYXkgZmlyc3QgNiByb3dzDQpgYGANCg0KIyMgVmlzdWFsaXNhdGlvbnMgb2YgdGVybXMgZnJlcXVlbmN5DQoNCiMjIyBCYXIgY2hhcnRzIHdpdGggdGhlIG1vc3QgZnJlcXVlbnQgdGVybXMNCg0KV2Ugd291bGQgYmUgaW50ZXJlc3RlZCBpbiB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyB1c2VkIGluIGN1c3RvbWVyIHJldmlld3MuIFNvbWV0aW1lcyBqdXN0IGEgZ2xpbXBzZSBvZiB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBpcyBzdWZmaWNpZW50IHRvIGdldCBzb21lIGluc2lnaHRzLiANCg0KSGVyZSB3ZSBzZWUgdGhhdCB3b3JkICJsb3ZlIiBhbmQgImdyZWF0IiBhcHBlYXJzIGFtb25nIG1vc3QgZnJlcXVlbnQgdGVybXMuDQoNCmBgYHtyfQ0KIyBNb3N0IGZyZXF1ZW50IHRlcm1zIC0tLS0NCnJldmlldy50ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KHJldmlldy5jb3JwdXMpDQpyZXZpZXcubSA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChyZXZpZXcudGRtKQ0KcmV2aWV3LnYgPC0gc29ydChyb3dTdW1zKHJldmlldy5tKSxkZWNyZWFzaW5nPVRSVUUpDQp3b3JkLm5hbWVzPC1uYW1lcyhyZXZpZXcudikNCmRmLnJldmlldy52PC1kYXRhLmZyYW1lKHJldmlldy52LHdvcmQubmFtZXMpDQpjb2xuYW1lcyhkZi5yZXZpZXcudik8LWMoIm4iLCJ3b3JkIikNCnA8LWdncGxvdChkYXRhPWRmLnJldmlldy52WzE6MjAsXSwgYWVzKHg9cmVvcmRlcih3b3JkLG4pLCB5PW4pKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IixmaWxsPSJzdGVlbGJsdWUiKSArIA0KICBjb29yZF9mbGlwKCkgKyANCiAgZ2d0aXRsZSgiMjAgbW9zdCBmcmVxdWVudCB3b3JkcyBpbiBjdXN0b21lciByZXZpZXdzIC0gTWFjQm9vayBQcm8iKSsNCiAgeGxhYigiQ291bnQiKSsNCiAgeWxhYigiV29yZCIpKw0KICB0aGVtZV90ZXN0KCkNCmdncGxvdGx5KHApDQoNCmBgYA0KDQojIyMgV29yZGNsb3VkIHdpdGggdGhlIG1vc3QgZnJlcXVlbnQgdGVybXMNCg0KU2ltaWxhcmx5IHRvIHRoZSBiYXIgY2hhcnQgd2l0aCB0aGUgbW9zdCBmcmVxdWVudCB3b3Jkcywgd2UgY291bGQgdXNlICoqd29yZGNsb3VkKiogYXMgd2VsbC4gSXQgZGlzcGxheXMgd29yZHMgZnJvbSB0aGUgY29ycHVzIGFuZCBzaWduYWxpemVzIHRoZWlyIGZyZXF1ZW5jeSBieSBkaXNwbGF5aW5nIG1vcmUgZnJlcXVlbnQgd29yZHMgYmlnZ2VyIHJlbGF0aXZlIHRvIHRob3NlIHRoYXQgYXBwZWFyIGxlc3MgZnJlcXVlbnRseSBpbiB0aGUgY29ycHVzLiBJbiB0aGUgd29yZGNsb3VkIGJlbG93IHlvdSBjYW4gc2VlIDIwMCBtb3N0IGZyZXF1ZW50IHdvcmRzLCB3aGVyZSB0aGUgbWluaW11bSBmcmVxdWVuY3kgd2FzIHNldCB0byAxLg0KDQpgYGB7cn0NCiMgV29yZGNsb3VkDQpyZXZpZXcudGRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChyZXZpZXcuY29ycHVzKQ0KcmV2aWV3Lm0gPC0gYXMubWF0cml4KHJldmlldy50ZG0pDQpyZXZpZXcudiA8LSBzb3J0KHJvd1N1bXMocmV2aWV3Lm0pLGRlY3JlYXNpbmc9VFJVRSkNCnJldmlldy5kIDwtIGRhdGEuZnJhbWUod29yZCA9IG5hbWVzKHJldmlldy52KSxmcmVxPXJldmlldy52KQ0Kc2V0LnNlZWQoMTIzNCkNCndvcmRjbG91ZCh3b3JkcyA9IHJldmlldy5kJHdvcmQsIGZyZXEgPSByZXZpZXcuZCRmcmVxLCBtaW4uZnJlcSA9IDEsDQogICAgICAgICAgbWF4LndvcmRzPTIwMCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuMzUsDQogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQoNCmBgYA0KDQojIyMgVGhlIG1vc3QgZnJlcXVlbnQgdGVybXMgaW5kaWNhdGluZyBlbW90aW9ucw0KDQpXaGVuIGl0IGNvbWVzIHRvIGFudGljaXBhdGlvbiwgd29yZHMgc3VjaCBhcyAiZ29vZCIsInRpbWUiLCJoYXBweSIgb3IgInBvd2VyZnVsIiBpbmRpY2F0ZXMgdGhhdCB0aGlzIGVtb3Rpb24gY2FuIGJlIGlkZW50aWZpZWQgYW1vbmcgY3VzdG9tZXIgcmV2aWV3cy4gT24gdGhlIG90aGVyIGhhbmQsIHRoZXJlIGFyZSBzb21lIHdvcmRzIHRoYXQgY291bGQgYmUgYSBzaWduYWwgYm90aCBmb3IgZ29vZCBhbmQgYmFkIGV4cGVyaWVuY2U6ICJmaW5hbGx5IiwibW9uZXkiIG9yICJ3YWl0Ii4gDQoNCmBgYHtyfQ0KIyBBbnRpY2lwYXRpb24gd29yZHMtLS0tDQpucmMuYW50aWNpcGF0aW9uIDwtIHN1YnNldChucmMsIG5yYyRzZW50aW1lbnQ9PSJhbnRpY2lwYXRpb24iKQ0KcmV2aWV3LmFudGljaXBhdGlvbi53b3JkcyA8LSBpbm5lcl9qb2luKHJldmlldy50aWR5LCBucmMuYW50aWNpcGF0aW9uKQ0KcmV2aWV3LmFudGljaXBhdGlvbi53b3JkcyA8LSBjb3VudChyZXZpZXcuYW50aWNpcGF0aW9uLndvcmRzLCB3b3JkKQ0KcmV2aWV3LmFudGljaXBhdGlvbi53b3JkcyA8LSByZXZpZXcuYW50aWNpcGF0aW9uLndvcmRzW29yZGVyKHJldmlldy5hbnRpY2lwYXRpb24ud29yZHMkbixkZWNyZWFzaW5nID0gVFJVRSksXQ0KcDwtZ2dwbG90KGRhdGE9cmV2aWV3LmFudGljaXBhdGlvbi53b3Jkc1sxOjIwLF0sIGFlcyh4PXJlb3JkZXIod29yZCxuKSwgeT1uKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsZmlsbD0ib3JhbmdlIikgKyANCiAgY29vcmRfZmxpcCgpICsgDQogIGdndGl0bGUoIjIwIG1vc3QgZnJlcXVlbnQgYW50aWNpcGF0aW9uIHdvcmRzIGluIGN1c3RvbWVyIHJldmlld3MiKSsNCiAgeGxhYigiQ291bnQiKSsNCiAgeWxhYigiV29yZCIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmdncGxvdGx5KHApDQpgYGANCg0KU2ltaWxhcmx5IHRvIGFudGljaXBhdGlvbiwgbm93IHdlIG9ic2VydmUgYSBsaXN0IG9mIHRvcCAyMCB3b3JkcyB0aGF0IGluZGljYXRlIHRydXN0LiBJdCByZXZlYWxzIG5ldyBxdWl0ZSBmcmVxdWVudCB0ZXJtIGluIHRoZSBjb3JwdXM6ICJyZWNvbW1lbmQiLiANCg0KYGBge3J9DQojIFRydXN0IHdvcmRzLS0tLQ0KbnJjLnRydXN0IDwtIHN1YnNldChucmMsIG5yYyRzZW50aW1lbnQ9PSJ0cnVzdCIpDQpyZXZpZXcudHJ1c3Qud29yZHMgPC0gaW5uZXJfam9pbihyZXZpZXcudGlkeSwgbnJjLnRydXN0KQ0KcmV2aWV3LnRydXN0LndvcmRzIDwtIGNvdW50KHJldmlldy50cnVzdC53b3Jkcywgd29yZCkNCnJldmlldy50cnVzdC53b3JkcyA8LSByZXZpZXcudHJ1c3Qud29yZHNbb3JkZXIocmV2aWV3LnRydXN0LndvcmRzJG4sZGVjcmVhc2luZyA9IFRSVUUpLF0NCnA8LWdncGxvdChkYXRhPXJldmlldy50cnVzdC53b3Jkc1sxOjIwLF0sIGFlcyh4PXJlb3JkZXIod29yZCxuKSwgeT1uKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsZmlsbD0icm95YWxibHVlMSIpICsgDQogIGNvb3JkX2ZsaXAoKSArIA0KICBnZ3RpdGxlKCIyMCBtb3N0IGZyZXF1ZW50IHRydXN0IHdvcmRzIGluIGN1c3RvbWVyIHJldmlld3MiKSsNCiAgeGxhYigiQ291bnQiKSsNCiAgeWxhYigiV29yZCIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmdncGxvdGx5KHApDQpgYGANCg0KQWx0aG91Z2ggYXQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdCwgIlRoZSB0b3AgMjAgbGlzdCIgb2Ygam95IHdvcmRzIGRpc3BsYXlzIHNvbWUgYWRkaXRpb25hbCB3b3JkcyB0aGF0IHdlIGRpZCBub3Qgb2JzZXJ2ZSBwcmV2aW91c2x5IHN1Y2ggYXMgImJlYXV0aWZ1bCIsImdvcmdlb3VzIiwid29uZGVyZnVsIiwiaW1wcm92ZW1lbnQiLCJleGNlbGxlbnQiLg0KDQpgYGB7cn0NCiMgSm95IHdvcmRzIC0tLS0NCm5yYy5qb3kgPC0gc3Vic2V0KG5yYywgbnJjJHNlbnRpbWVudD09ImpveSIpDQpyZXZpZXcuam95LndvcmRzIDwtIGlubmVyX2pvaW4ocmV2aWV3LnRpZHksIG5yYy5qb3kpDQpyZXZpZXcuam95LndvcmRzIDwtIGNvdW50KHJldmlldy5qb3kud29yZHMsIHdvcmQpDQpyZXZpZXcuam95LndvcmRzIDwtIHJldmlldy5qb3kud29yZHNbb3JkZXIocmV2aWV3LmpveS53b3JkcyRuLGRlY3JlYXNpbmcgPSBUUlVFKSxdDQpwPC1nZ3Bsb3QoZGF0YT1yZXZpZXcuam95LndvcmRzWzE6MjAsXSwgYWVzKHg9cmVvcmRlcih3b3JkLG4pLCB5PW4pKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IixmaWxsPSJkYXJrb3JhbmdlMSIpICsgDQogIGNvb3JkX2ZsaXAoKSArIA0KICBnZ3RpdGxlKCIyMCBtb3N0IGZyZXF1ZW50IHRydXN0IHdvcmRzIGluIGN1c3RvbWVyIHJldmlld3MiKSsNCiAgeGxhYigiQ291bnQiKSsNCiAgeWxhYigiV29yZCIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmdncGxvdGx5KHApDQpgYGANCg0KIyMgU2VudGltZW50IGFuYWx5c2lzDQoNCiMjIyBQb2xhcml0eSB0aW1lbGluZQ0KDQpPbmUgdXN1YWwgd2F5IHRvIGNvbXBhcmUgYW5kIHF1YW50aWZ5IGVtb3Rpb25zIGluIHRleHQgaXMgdmlhIHBvbGFyaXR5LiBXZSBzaW1wbHkgY291bnQgbnVtYmVyIG9mIHVuaXF1ZSB3b3JkcyBpbiBlYWNoIGRvY3VtZW50ICg9cmV2aWV3KSBsYWJlbGxlZCBhcyBuZWdhdGl2ZSBhbmQgZGVkdWN0IGZyb20gdGhlIGNvdW50IG9mIHVuaXF1ZSBwb3NpdGl2ZSB3b3Jkcy4gRm9yIGluc3RhbmNlLCB0aGUgZmlyc3QgcmV2aWV3IGNvbnRhaW5zIDIgdW5pcXVlIHBvc2l0aXZlIHdvcmRzICgiZ3JlYXQiIGFuZCAic3Ryb25nIikgYW5kIG5vbmUgbmVnYXRpdmUgdW5pcXVlIHdvcmRzLiBUaGVyZWZvcmUsIGl0cyBwb2xhcml0eSBzY29yZSBpcyAyLg0KDQpUaGlzIHBvbGFyaXR5IHRpbWVsaW5lIHN1Z2dlc3RzIHZlcnkgaW1wb3J0YW50IGltcGxpY2F0aW9uOiB0aGUgcmV2aWV3cycgc2VudGltZW50IGlzIG1vdmluZyBhYm92ZSB0aGUgMCwgYmVhcmx5IGdvaW5nIGV2ZW4gYmVsb3cgKzIsIGdpdmluZyBhbiBpbmRpY2F0aW9uIHRoYXQgdGhpcyBwcm9kdWN0IGNvbnRpbnVvdXNseSBtZWV0IGN1c3RvbWVycycgZXhwZWN0YXRpb25zLiBUaGF0IGlzIGEgZ29vZCBzaWduYWwgdG8gYmVsaWV2ZSB0aGF0IGN1c3RvbWVycyBhcmUgcmF0aGVyIHNhdGlzZmllZCB3aXRoIHRoZSBwcm9kdWN0LiAgICANCg0KYGBge3J9DQojIFBvbGFyaXR5IHRpbWVsaW5lIC0tLS0NCnJldmlldy5zZW50aW1lbnQgPC0gaW5uZXJfam9pbihyZXZpZXcudGlkeSwgYmluZykNCnJldmlldy5zZW50aW1lbnQgPC0gY291bnQocmV2aWV3LnNlbnRpbWVudCwgc2VudGltZW50LCBpbmRleD1kb2N1bWVudCkNCnJldmlldy5zZW50aW1lbnQgPC0gc3ByZWFkKHJldmlldy5zZW50aW1lbnQsIHNlbnRpbWVudCwgbiwgZmlsbD0wKQ0KcmV2aWV3LnNlbnRpbWVudCRwb2xhcml0eSA8LSByZXZpZXcuc2VudGltZW50JHBvc2l0aXZlIC0gcmV2aWV3LnNlbnRpbWVudCRuZWdhdGl2ZQ0KcmV2aWV3LnNlbnRpbWVudCRwb3MgPC0gaWZlbHNlKHJldmlldy5zZW50aW1lbnQkcG9sYXJpdHkgPj0wLCAiUG9zaXRpdmUiLCAiTmVnYXRpdmUiKQ0KcDwtZ2dwbG90KHJldmlldy5zZW50aW1lbnQsIGFlcyh4PWluZGV4LCB5PXBvbGFyaXR5LCBmaWxsPXBvcykpK2dlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImlkZW50aXR5Iiwgd2lkdGg9MSkrdGhlbWVfZ2RvY3MoKStnZ3RpdGxlKGxhYmVsPSJQb2xhcml0eSB0aW1lbGluZSIpDQpnZ3Bsb3RseShwKQ0KDQojIFNtb290aCBjdXJ2ZQ0KcmV2aWV3LnNtb290aCA8LSBnZ3Bsb3QocmV2aWV3LnNlbnRpbWVudCwgYWVzKGluZGV4LCBwb2xhcml0eSkpDQpwPC1yZXZpZXcuc21vb3RoICsgc3RhdF9zbW9vdGgoKSArIHRoZW1lX2dkb2NzKCkgKyBnZ3RpdGxlKCJQb2xhcml0eSB0aW1lbGluZSAtIHNtb290aCIpDQpnZ3Bsb3RseShwKQ0KDQpgYGANCg0KSW4gdGhlIHBvbGFyaXR5IGdyYXBoIGF0IGluZGV4IDgxIHdlIGlkZW50aWZ5IGEgcmV2aWV3IHdpdGggc2VudGltZW50IHNjb3JlIG9mIGV2ZW4gMzQhIFRoaXMgc2VlbXMgdG8gYmUgYSB0aHJpbGxlZCBjdXN0b21lciBldmVyeSBicmFuZCBsb3ZlcyEgTGV0IHVzIHRha2UgY2xvc2VyIGxvb2s6DQoNCmBgYHtyfQ0KcmV2aWV3LnNlbnRpbWVudCA8LSBpbm5lcl9qb2luKHJldmlldy50aWR5LCBiaW5nKQ0KZG9jXzgxPC1maWx0ZXIocmV2aWV3LnNlbnRpbWVudCwgZG9jdW1lbnQ9PSI4MSIpDQpoZWFkKGRvY184MVtvcmRlcihkb2NfODEkY291bnQsZGVjcmVhc2luZyA9IFQpLF0pDQpgYGANCg0KRmluYWxseSwgaXQgY2VydGFpbmx5IHBheXMgb2ZmIHRvIGNoZWNrIHRoZSBhY3R1YWwgcmV2aWV3Og0KDQpgYGB7ciwgb3V0LndpZHRoPSI1MCUifQ0KIyBPdXRsaWVyIGluIHBvbGFyaXR5IHNjb3JlDQpyZXZpZXckcmV2aWV3X3RleHRbODFdDQpgYGANCg0KSXQgc2VlbXMgdGhhdCBvdXIgYXNzdW1wdGlvbiB3YXMgY29ycmVjdCEgVGhlIGN1c3RvbWVyIHdhcyBkZWZpbml0ZWx5IHRocmlsbGVkISBUaGlzIGlzIGEgbmljZSBleGFtcGxlIGhvdyB5b3UgY2FuIGlkZW50aWZ5IGFuZCB0YWtlIGNsb3NlciBsb29rIGF0IHJldmlld3MgdGhhdCBzdGFuZCBvdXQgYmFzZWQgb24gaXRzIHBvbGFyaXR5IHNjb3JlLg0KDQojIyMgQW5hbHlzaXMgb24gc2VudGVuY2UtbGV2ZWwNCg0KVGV4dCBhbmFseXNpcyBwcm92aWRlcyBmcmVlZG9tIHRvIGNob29zZSBsZXZlbCBvZiBvYnNlcnZhdGlvbi4gU28gZmFyLCB3ZSBleHBsb3JlZCB3b3JkcyBhbmQgdGhlaXIgZnJlcXVlbmNpZXMsIHdlIGV4cGxvcmVkIGN1c3RvbWVyIHJldmlld3MgYW5kIHF1YW50aWZpZWQgdGhlaXIgc2VudGltZW50IGluIHR3byBkaW1lbnNpb25zIChwb3NpdGl2ZSBhbmQgbmVnYXRpdmUpLiBOZXh0LCB3ZSB3aWxsIGFwcHJvYWNoIHRoZSB0YXNrIG9mIGlkZW50aWZ5aW5nIHRoZSBtb3N0IG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSByZXZpZXdzIGJ5IG9yZ2FuaXppbmcgdGV4dCBieSBzZW50ZW5jZXMuIEJ5IGRvaW5nIHNvLCB3ZSB3aWxsIGRpcmVjdGx5IGFjY2VzcyB0aG9zZSBzZW50ZW5jZXMgd2hvc2UgYXZlcmFnZSBzZW50aW1lbnQgc3RhbmQgb3V0LiAgIA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2Ugc2VudGltZW50DQpyZXZpZXcuaGlnaGxpZ2h0ZWQ8LXJldmlldyRyZXZpZXdfdGV4dCU+JQ0KICBnZXRfc2VudGVuY2VzKCkgJT4lDQogIHNlbnRpbWVudF9ieSgpDQpoZWFkKHJldmlldy5oaWdobGlnaHRlZCkNCmBgYA0KDQpgYGB7cn0NCiMgUHJlcGFyaW5nIGRhdGENCnJldmlldy5zY29yZSA8LSBzdWJzZXQocmV2aWV3LmhpZ2hsaWdodGVkLCBzZWxlY3QgPSBjKCJhdmVfc2VudGltZW50IiwiZWxlbWVudF9pZCIpKQ0KcmV2aWV3LndvcnN0IDwtIHJldmlldy5zY29yZVtvcmRlcihyZXZpZXcuc2NvcmUkYXZlX3NlbnRpbWVudCxkZWNyZWFzaW5nID0gRkFMU0UpLF0NCnJldmlldy53b3JzdDwtcmV2aWV3LndvcnN0JGVsZW1lbnRfaWRbMToxMF0NCnJldmlldy5iZXN0IDwtIHJldmlldy5zY29yZVtvcmRlcihyZXZpZXcuc2NvcmUkYXZlX3NlbnRpbWVudCwgZGVjcmVhc2luZyA9IFRSVUUpLF0NCnJldmlldy5iZXN0IDwtIHJldmlldy5iZXN0JGVsZW1lbnRfaWRbMToxMF0NCnNlbnRlbmNlczwtcmV2aWV3JHJldmlld190ZXh0ICU+JSBnZXRfc2VudGVuY2VzKCkNCnNlbnRlbmNlczwtYXMubWF0cml4KHNlbnRlbmNlcykNCmBgYA0KDQpBbmQgaGVyZSB3ZSBoYXZlICJ0aGUgd29yc3QgMTAgc2VudGVuY2VzIiBmcm9tIGN1c3RvbWVyIHJldmlld3M7DQpgYGB7cn0NCiMgMTAgd29yc3Qgc2VudGVuY2VzDQpzZW50ZW5jZXNbcmV2aWV3LndvcnN0XQ0KYGBgDQpEZXNwaXRlIHRoZSBmYWN0IHRoYXQgcG9zaXRpdmUgc2VudGltZW50IHByZXZhaWxzLCB3ZSBzZWUgdGhhdCB0aGVyZSBhcmUgY2VydGFpbiBwcm9ibGVtcyBhc3NvY2lhdGVkIHdpdGggTWFjQm9vayBsYXB0b3AuIElzc3VlcyB3aXRoIHNjcmVlbiwgcHJvYmxlbXMgd2l0aCB3b29mZXJzLCBkaXNhcHBvaW50bWVudCB0aGF0IHRoZXJlIGFyZSBubyBwb3J0cywgdW5zYXRpc2Z5aW5nIHZhbHVlLXByaWNlIHJhdGlvLg0KDQpgYGB7cn0NCiMgMTAgbW9zdCBwb3NpdGl2ZSBzZW50ZW5jZXMNCnNlbnRlbmNlc1tyZXZpZXcuYmVzdF0NCmBgYA0KSWYgd2UgdGFrZSBhIGxvb2sgYXQgIjEwIG1vc3QgcG9zaXRpdmUgc2VudGVuY2VzIiBmcm9tIGN1c3RvbWVyIHJldmlld3MsIHdlIHdvdWxkIGZpbmQgYSBzaW1pbGFyIGV2aWRlbmNlIGFzIHdlIG9idGFpbmVkIHdpdGggcG9sYXJpdHkgc2NvcmUuIEhvd2V2ZXIsIGJ5IHJlYWRpbmcgdGhvc2Ugc2VudGVuY2VzIGEgcmVhZGVyIGNhbiBoYXZlIGJldHRlciBmZWVsaW5nIHdoYXQgdGhlIHJldmlld2VyIGlzIGFjdHVhbGx5IHNhdGlzZmllZCBvciB1bnNhdGlzZmllZCB3aXRoLiBIZXJlIHdlIHNlZSB0aGF0IHNvbWUgcGVvcGxlIGFkbWlyZSB0aGUgc3BlZWQgZm9yIGluc3RhbmNlLg0KDQojIyMgV2hhdCBhcmUgdGhlIG1vc3QgZW1vdGlvbmFsIHJldmlld3M/DQoNClBhY2thZ2UgYHNlbnRpbWVudHJgIHByb3ZpZGVzIG5pY2UgZnVuY3Rpb24gYGVtb3Rpb24oKWAgd2hpY2ggdXNlcyBhIGRpY3Rpb25hcnkgdG8gZmluZCBlbW90aW9uIHdvcmRzIGFuZCB0aGVuIGNvbXB1dGUgdGhlIHJhdGUgcGVyIHNlbnRlbmNlLiBUaGUgZmluYWwgZW1vdGlvbiBzY29yZSByYW5nZXMgYmV0d2VlbiAwIChubyBlbW90aW9uIHVzZWQpIGFuZCAxIChhbGwgd29yZHMgdXNlZCB3ZXJlIGVtb3Rpb25hbCkuDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IGVtb3Rpb25zIHRlcm1zDQpyZXZpZXdzLmVtb3Rpb24gPC0gcmV2aWV3JHJldmlld190ZXh0ICU+JSBnZXRfc2VudGVuY2VzKCkgJT4lIGVtb3Rpb24oKQ0KDQojIFRvcCA1MCBzZW50ZW5jZXMgd2l0aCB0aGUgaGlnaGVzdCBlbW90aW9uIHNjb3JlIA0KdG9wX2Vtb3Rpb25hbF9zZW50ZW5jZXMgPC0gdW5pcXVlKHJldmlld3MuZW1vdGlvbltvcmRlcihyZXZpZXdzLmVtb3Rpb24kZW1vdGlvbixkZWNyZWFzaW5nID0gVFJVRSksXSRlbGVtZW50X2lkWzE6NTBdKQ0KDQojIFRoZSBtb3N0IGVtb3Rpb25hbCByZXZpZXdzDQpzZW50ZW5jZXNbdG9wX2Vtb3Rpb25hbF9zZW50ZW5jZXMsXQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCBpZGVudGlmaWVkIHNlbnRlbmNlcyB2ZXJ5IGNsZWFybHkgcmVmbGVjdCBlbW90aW9ucyB0aGF0IGN1c3RvbWVycyBleHByZXNzZWQuIEl0IHNlZW1zIHRoYXQgaW50ZW5zaXR5IG9mIGVtb3Rpb25zIGlzIGhpZ2ggaW4gYm90aCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZGlyZWN0aW9uLg0KDQpGaW5hbGx5LCB3ZSBjYW4gcGxvdCBkZXRlY3RlZCBlbW90aW9ucyBpbiBvcmRlciB0byBnZXQgYSBiaXQgbW9yZSBjbGVhciBpbnNpZ2h0IGluIGVtb3Rpb25hbCBzdHJ1Y3R1cmUgZGV0ZWN0ZWQgaW4gdGhlIHJldmlld3M6DQoNCmBgYHtyfQ0KIyBQbG90IG9mIGVtb3Rpb24NCnBsb3QocmV2aWV3cy5lbW90aW9uLA0KICAgICB0cmFuc2Zvcm1hdGlvbi5mdW5jdGlvbiA9IHN5dXpoZXQ6OmdldF9kY3RfdHJhbnNmb3JtLA0KICAgICBkcm9wLnVudXNlZC5lbW90aW9ucyA9IFRSVUUsIGZhY2V0ID0gVFJVRSkNCmBgYA0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gDQoNClRoaXMgcGxvdCBkZXBpY3RzIGVtb3Rpb25hbCBpbmNsaW5hdGlvbiBvZiB0aGUgcmV2aWV3cy4gQ3VydmVzIGluZGljYXRpbmcgZW1vdGlvbmFsIHByb3BlbnNpdHkgb2YgdHJ1c3QsIGpveSBhbmQgYW50aWNpcGF0aW9uIHN1Z2dlc3Qgc3Ryb25nIGluY2xpbmF0aW9uIHRvd2FyZHMgbWVudGlvbmVkIGVtb3Rpb25zLiBJbiBsaW5lIHdpdGggdGhlIGFjYWRlbWljIHBhcGVyIFtUaGUgUm9sZSBvZiBFbW90aW9ucyBmb3IgdGhlIFBlcmNlaXZlZCBVc2VmdWxuZXNzIGluIE9ubGluZSBDdXN0b21lciBSZXZpZXdzXShodHRwczovL3d3dy5qc3Rvci5vcmcvc3RhYmxlL3BkZi8yMDYxOTA5NS5wZGYpLCB3ZSBoYXZlIGZvdW5kIGFuIGV2aWRlbmNlIHRoYXQgb25saW5lIHJldmlld3MgYW5hbHl6ZWQgZW5jb2RlIGVtb3Rpb25zIGNvbnRyaWJ1dGluZyB0byB0aGUgaGlnaGVyIGhlbHBmdWxuZXNzIHJhdGluZywgaS5lLiBxdWFsaXR5IG9mIHJldmlld3MuDQoNCkluIHRoZSBlbmQsIGl0IGlzIGltcG9ydGFudCB0byBtZW50aW9uIHRoYXQgdGhlIHJvbGUgYW5kIGluZmx1ZW5jZSBvZiBlbW90aW9ucyBkZWZpbmVkIGJ5IFBsdXRjaGlrIG9uIHRoZSBxdWFsaXR5IG9mIHJldmlld3MgZGlmZmVyIGFjcm9zcyBkaWZmZXJlbnQgcHJvZHVjdCBjYXRlZ29yaWVzLiANCg0KPC9kaXY+IA0KDQoNCg==